﻿using System.Collections.Generic;
using System.Linq;
using Cms.Application.Admin.Views;
using Cms.Application.IO;
using Cms.Data;
using Cms.Domain;
using Cms.Domain.RepositoryInterfaces;

namespace Cms.Application.Admin.Presenters
{
    public class ExplorerPresenter : BasePresenter
    {
        private readonly Administrator _administrator;
        private readonly IExplorerView _view;

        public IDiskAccess DiskAccess { get; set; }

        public ExplorerPresenter(IExplorerView view)
            : this(view, new RepositoryFactory())
        {
            DiskAccess = new DiskAccess();
        }

        public ExplorerPresenter(IExplorerView view, IRepositoryFactory repositoryFactory)
            : base(view, repositoryFactory)
        {
            _view = view;
            _administrator = AdministratorRepository.GetAdministrator(_view.AdministratorID);
            if (!_administrator.HasPermission.Explorer)
                _view.RedirectToPermissionDeniedPage();
            if (!_administrator.HasPermission.ExplorerAddFolder)
                _view.ExplorerAddFolderNotAllowed();
        }

        public void Initialize()
        {
            if (FolderRepository.GetFolder(_administrator.SelectedFolderID) != null)
                _view.SelectedFolderID = _administrator.SelectedFolderID;
            else
                _view.SelectedFolderID = -1;
            SetFolders();
            OpenFolder();
            if (_administrator.CurrentSite.MasterTemplates.Count == 0)
                _view.DisplayCreateMasterTemplateAlert();
            else if (_administrator.CurrentSite.DetailTemplates.Count == 0)
                _view.DisplayCreateDetailTemplateAlert();
        }

        public void OpenFolder()
        {
            _administrator.SelectedFolderID = _view.SelectedFolderID;
            AdministratorRepository.SaveAdministrator(_administrator);
            IList<Page> pagesInSelectedFolder = PageRepository.GetPages(_administrator.CurrentSite.ID, _view.SelectedFolderID);
            IList<Page> allowedPages = GetAllowedPages(pagesInSelectedFolder);
            _view.SetPages(allowedPages);
        }
        
        public IList<Page> GetAllowedPages(IList<Page> pages)
        {
            IList<Page> allowedPages = new List<Page>();
            foreach (Page page in pages)
            {
                if (page.AllowsAdministrator(_administrator))
                {
                    allowedPages.Add(page);
                }
            }
            return allowedPages;
        }

        public void SetFolders()
        {
            Locale locale = LocaleRepository.GetLocale(_view.LocaleID);
            string localeCode = string.Empty;
            if (locale != null && locale != _administrator.CurrentSite.DefaultLocale)
            {
                localeCode = locale.UrlCode;
            }
            _view.SetFolders(_administrator.CurrentSite.Domain, GetAllowedRootFolders(), localeCode);
        }
        
        public bool IsFolderClosed(int folderId)
        {
            return _administrator.ClosedFolders.Any(id => id == folderId);
        }

        public void ToggleFolder(int folderId)
        {
            if (_administrator.ClosedFolders.Contains(folderId))
            {
                _administrator.ClosedFolders.Remove(folderId);
            }
            else
            {
                _administrator.ClosedFolders.Add(folderId);
            }
            AdministratorRepository.SaveAdministrator(_administrator);
            SetFolders();
        }

        public IList<Folder> GetAllowedRootFolders()
        {
            return GetAllowedFolders(SiteRepository.GetRootFoldersForExplorer(_administrator.CurrentSite));
        }

        private IList<Folder> GetAllowedFolders(IList<Folder> folders)
        {
            IList<Folder> allowedFolders = new List<Folder>();
            foreach (Folder folder in folders)
            {
                if (folder.AllowsAdministrator(_administrator))
                {
                    allowedFolders.Add(folder);
                    IList<Folder> allowedSubFolders = GetAllowedFolders(folder.SubFolders);
                    folder.SubFolders.Clear();
                    foreach (Folder allowedSubFolder in allowedSubFolders)
                    {
                        folder.SubFolders.Add(allowedSubFolder);
                    }
                }
            }
            return allowedFolders;
        }

        public void Move(string draggedID, string dropzoneID)
        {
            Folder targetFolder = dropzoneID == "domain" ? null : FolderRepository.GetFolder(int.Parse(dropzoneID.Substring(1)));

            // Check if the dragged item is a Folder or a Page
            switch (draggedID.Substring(0, 1))
            {
                case "F": // Folder
                    Folder movedFolder = FolderRepository.GetFolder(int.Parse(draggedID.Substring(1)));
                    if (movedFolder != null)
                    {
                        if (movedFolder.IsParentTo(targetFolder))
                        {
                            AddValidationError(1, GetText(_view, "IsParentToTargetError"));
                        }
                        if (!_administrator.CurrentSite.CanFolderBeNamedAndMovedTo(movedFolder.ID, movedFolder.Name, targetFolder))
                        {
                            AddValidationError(2, GetText(_view, "MoveFolder_NameCollisionError"));
                        }
                        if (!IsValid)
                        {
                            break;
                        }
                        string originalVirtualPath = movedFolder.GetVirtualPath();
                        IDictionary<int, string> originalPaths = new Dictionary<int, string>();
                        CollectOriginalPaths(movedFolder, ref originalPaths);
                        if (movedFolder.Parent != null)
                        {
                            movedFolder.Parent.SubFolders.Remove(movedFolder);
                        }
                        if (targetFolder != null)
                        {
                            targetFolder.SubFolders.Add(movedFolder);
                        }
                        movedFolder.Parent = targetFolder;
                        SiteRepository.SaveSite(movedFolder.Site);
                        //DiskAccess.MoveFolder(originalVirtualPath, movedFolder.GetVirtualPath());
                        UpdatePageReferences(originalPaths);
                    }
                    break;
                case "P": // Page
                case "A": // ApplicationPage
                    Page movedPage = PageRepository.GetPage(int.Parse(draggedID.Substring(1)));
                    if (!_administrator.CurrentSite.CanPageBeNamedAndMovedTo(movedPage.ID, movedPage.Filename, targetFolder))
                    {
                        AddValidationError(3, GetText(_view, "MovePage_NameCollisionError"));
                    }
                    if (!IsValid)
                    {
                        break;
                    }
                    if (movedPage != null)
                    {
                        string originalVirtualPath = movedPage.GetVirtualPath();
                        if (movedPage.Folder != null)
                            movedPage.Folder.Pages.Remove(movedPage);
                        if (targetFolder != null)
                            targetFolder.Pages.Add(movedPage);
                        movedPage.Folder = targetFolder;
                        PageRepository.SavePage(movedPage);
                        var frm = new FileReferencesManager(_administrator, SiteRepository, ContentRepository, DiskAccess);
                        frm.UpdateFileReferences(originalVirtualPath, movedPage.GetVirtualPath(), _administrator.CurrentSite.ID);
                    }
                    OpenFolder();
                    break;
            }
            SetFolders();
        }
        
        public void DeleteFolder(int folderID)
        {
            Folder deletedFolder = FolderRepository.GetFolder(folderID);
            ValidateDeleteFolder(deletedFolder);
            if (!IsValid)
            {
                return;
            }

            // All is good!
            string deletedFolderPath = deletedFolder.GetVirtualPath();
            IDictionary<int, string> originalPaths = new Dictionary<int, string>();
            CollectOriginalPaths(deletedFolder, ref originalPaths);

            // Move all subfolders to the parent folder
            foreach (Folder subfolder in deletedFolder.SubFolders)
            {
                subfolder.Parent = deletedFolder.Parent;
                if (deletedFolder.Parent != null)
                {
                    deletedFolder.Parent.SubFolders.Add(subfolder);
                }
            }
            deletedFolder.SubFolders.Clear();

            // Move all pages to the parent folder
            foreach (Page p in deletedFolder.Pages)
            {
                p.Folder = deletedFolder.Parent;
                if (deletedFolder.Parent != null)
                {
                    deletedFolder.Parent.Pages.Add(p);
                }
            }
            deletedFolder.Pages.Clear();

            // Remove references to the deleted folder
            deletedFolder.Site = null;
            if (deletedFolder.Parent != null)
            {
                deletedFolder.Parent.SubFolders.Remove(deletedFolder);
            }

            // If the deleted folder was selected, move focus to the parent folder instead
            if (deletedFolder.ID == _view.SelectedFolderID)
            {
                if (deletedFolder.Parent == null)
                {
                    _view.SelectedFolderID = -1;
                }
                else
                {
                    _view.SelectedFolderID = deletedFolder.Parent.ID;
                }
            }
            _administrator.CurrentSite.Folders.Remove(deletedFolder);
            SiteRepository.SaveSite(_administrator.CurrentSite);
            UpdatePageReferences(originalPaths);
            SetFolders();
            OpenFolder();
            //DiskAccess.DeleteFolder(deletedFolderPath);
        }
        
        public void UpdatePageReferences(IEnumerable<KeyValuePair<int, string>> originalPaths)
        {
            var frm = new FileReferencesManager(_administrator, SiteRepository, ContentRepository, DiskAccess);
            foreach (var kvp in originalPaths)
            {
                string originalVirtualPath = kvp.Value;
                string newVirtualPath = PageRepository.GetPage(kvp.Key).GetVirtualPath();
                frm.UpdateFileReferences(originalVirtualPath, newVirtualPath, _administrator.CurrentSite.ID);
            }
        }

        public void DeletePage(int id)
        {
            Page page = PageRepository.GetPage(id);
            var frm = new FileReferencesManager(_administrator, SiteRepository, ContentRepository, DiskAccess);
            frm.RemoveFileReferences(page.GetVirtualPath(), _administrator.CurrentSite.ID);
            page.Site.Pages.Remove(page);
            page.Site = null;
            if (page.Folder != null)
            {
                page.Folder.Pages.Remove(page);
                page.Folder = null;
            }
            foreach (PageContentLink pl in page.Content)
            {
                pl.Page = null;
            }
            page.Content.Clear();
            page.DetailTemplate = null;
            page.MasterTemplate = null;
            if (page is ApplicationPage)
            {
                (page as ApplicationPage).Application.Pages.Remove((page as ApplicationPage));
                (page as ApplicationPage).Application = null;
            }
            PageRepository.DeletePage(id);
            SiteRepository.SaveSite(_administrator.CurrentSite);
            if (page is ApplicationPage)
                DiskAccess.DeleteFile("__System\\ApplicationPages\\" + id + ".aspx");
            OpenFolder();
        }

        public string GetPagePreviewUrl(int pageID)
        {
            Page page = PageRepository.GetPage(pageID);
            return "http://" + page.Site.Domain + "/" + page.GetVirtualPath();
        }

        private void CollectOriginalPaths(Folder folder, ref IDictionary<int, string> paths)
        {
            foreach (Page page in folder.Pages)
                paths.Add(page.ID, page.GetVirtualPath());
            foreach (Folder f in folder.SubFolders)
                CollectOriginalPaths(f, ref paths);
        }

        private void ValidateDeleteFolder(Folder deletedFolder)
        {
            // Check for name collisions between subfolders and folders at the parent level
            bool hasSameName = false;
            IList<Folder> parentLevelFolders = deletedFolder.Parent == null
                                                   ? deletedFolder.Site.GetRootFolders()
                                                   : deletedFolder.Parent.SubFolders;
            foreach (Folder plf in parentLevelFolders)
            {
                if (plf.ID == deletedFolder.ID)
                {
                    continue;
                }
                foreach (Folder sf in deletedFolder.SubFolders)
                {
                    if (plf.Name.ToLower() != sf.Name.ToLower())
                    {
                        continue;
                    }
                    AddValidationError(3, GetText(_view, "DeleteFolderError_FolderNameCollision"));
                    hasSameName = true;
                    break;
                }
                if (hasSameName)
                {
                    break;
                }
            }
            hasSameName = false;

            // Check for name collisions between pages in the deleted folder and at the parent level
            IList<Page> parentLevelPages = deletedFolder.Parent == null
                                               ? deletedFolder.Site.GetRootPages()
                                               : deletedFolder.Parent.Pages;
            foreach (Page plp in parentLevelPages)
            {
                foreach (Page p in deletedFolder.Pages)
                {
                    if (plp.Filename.ToLower() != p.Filename.ToLower())
                        continue;
                    AddValidationError(4, GetText(_view, "DeleteFolderError_PageFilenameCollision"));
                    hasSameName = true;
                    break;
                }
                if (hasSameName)
                    break;
            }
        }
    }
}